#pragma once
#include <glad/glad.h>

class Shader;

class MeshRenderer
{
public:
    /**
     * 顶点格式 位置
     */
    const static uint32_t vertex_format_position;

    /**
     * 顶点格式 颜色
     */
    const static uint32_t vertex_format_color;

    /**
     * 顶点格式 法线
     */
    const static uint32_t vertex_format_normal;

    /**
     * 顶点格式 切线
     */
    const static uint32_t vertex_format_tangent;

    /**
     * uv1
     */
    const static uint32_t vertex_format_uv1;

    /**
     * uv2
     */
    const static uint32_t vertex_format_uv2;

    /**
     * uv3
     */
    const static uint32_t vertex_format_uv3;
public:
    void SetShader(Shader* shader) {
        this->shader = shader;
    }

    void SetVertex(float* vertex, int length) {
        this->vertex = vertex;
        this->vertexLength = length;
    }

    void SetIndices(uint32_t* indices, uint32_t length) {
        this->indices = indices;
        this->indicesLength = length;
    }

    void Init() {
        // VAO
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
        // VBO
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, static_cast<long long>(vertexLength * sizeof(float)), vertex, GL_STATIC_DRAW);
        // EBO
        glGenBuffers(1, &ebo);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, static_cast<long long>(indicesLength * sizeof(uint32_t)), indices, GL_STATIC_DRAW);

        // 设置顶点属性指针
        this->BindVertexAttribPoint();
    }

    void PreDraw() {
        shader->Use();
    }

    void Draw() {
        glBindVertexArray(vao);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
        glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indicesLength), GL_UNSIGNED_INT, nullptr);
    }

    Shader* GetShader() {
        return this->shader;
    }

    MeshRenderer(const uint32_t format) {
        this->vertex_format = format;
        if((this->vertex_format & vertex_format_position) == 0) {
            std::cerr << "MeshRenderer has at least one position property" << std::endl;
        }
    }
private:
    Shader* shader;
    // 顶点数组
    float* vertex;
    // 顶点数组的长度
    uint32_t vertexLength;

    // 索引数组
    uint32_t* indices;
    // 索引数组的长度
    uint32_t indicesLength;

    uint32_t vao;
    uint32_t ebo;
    uint32_t vbo;

    // 顶点的格式
    uint32_t vertex_format;

    /**
     * 获得当前网格一个顶点数据的大小
     * @return 网格顶点数据大小
     */
    uint32_t GetVertexSize() const {
        uint32_t size = 0;
        if((this->vertex_format & vertex_format_position) > 0)
            size += 3;
        if((this->vertex_format & vertex_format_color) > 0)
            size += 3;
        if((this->vertex_format & vertex_format_normal) > 0)
            size += 3;
        if((this->vertex_format & vertex_format_tangent) > 0)
            size += 3;
        if((this->vertex_format & vertex_format_uv1) > 0)
            size += 2;
        if((this->vertex_format & vertex_format_uv2) > 0)
            size += 2;
        if((this->vertex_format & vertex_format_uv3) > 0)
            size += 2;
        return size;
    }

    /**
     * 根据format设置顶点属性指针
     */
    void BindVertexAttribPoint() {
        // 设置顶点属性指针
        int dataSize = 0;
        int vertexDataSize = this->GetVertexSize();
        // Position
        if((this->vertex_format & vertex_format_position) > 0) {
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, static_cast<GLsizei>(vertexDataSize * sizeof(float)), nullptr);
            glEnableVertexAttribArray(0);
            dataSize += 3;
        }
        if((this->vertex_format & vertex_format_color) > 0) {
            glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, static_cast<GLsizei>(vertexDataSize * sizeof(float)), reinterpret_cast<void *>(dataSize * sizeof(float)));
            glEnableVertexAttribArray(1);
            dataSize += 3;
        }
        if((this->vertex_format & vertex_format_normal) > 0) {
            glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, static_cast<GLsizei>(vertexDataSize * sizeof(float)), reinterpret_cast<void *>(dataSize * sizeof(float)));
            glEnableVertexAttribArray(2);
            dataSize += 3;
        }
        if((this->vertex_format & vertex_format_tangent) > 0) {
            glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, static_cast<GLsizei>(vertexDataSize * sizeof(float)), reinterpret_cast<void *>(dataSize * sizeof(float)));
            glEnableVertexAttribArray(3);
            dataSize += 3;
        }
        if((this->vertex_format & vertex_format_uv1) > 0) {
            glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, static_cast<GLsizei>(vertexDataSize * sizeof(float)), reinterpret_cast<void *>(dataSize * sizeof(float)));
            glEnableVertexAttribArray(4);
            dataSize += 2;
        }
        if((this->vertex_format & vertex_format_uv2) > 0) {
            glVertexAttribPointer(5, 2, GL_FLOAT, GL_FALSE, static_cast<GLsizei>(vertexDataSize * sizeof(float)), reinterpret_cast<void *>(dataSize * sizeof(float)));
            glEnableVertexAttribArray(5);
            dataSize += 2;
        }
        if((this->vertex_format & vertex_format_uv3) > 0) {
            glVertexAttribPointer(6, 2, GL_FLOAT, GL_FALSE, static_cast<GLsizei>(vertexDataSize * sizeof(float)), reinterpret_cast<void *>(dataSize * sizeof(float)));
            glEnableVertexAttribArray(6);
            dataSize += 2;
        }
    }
};

const uint32_t MeshRenderer::vertex_format_position = 0x1;
const uint32_t MeshRenderer::vertex_format_color = 0x1 << 1;
const uint32_t MeshRenderer::vertex_format_normal = 0x1 << 2;
const uint32_t MeshRenderer::vertex_format_tangent = 0x1 << 3;
const uint32_t MeshRenderer::vertex_format_uv1 = 0x1 << 4;
const uint32_t MeshRenderer::vertex_format_uv2 = 0x1 << 5;
const uint32_t MeshRenderer::vertex_format_uv3 = 0x1 << 6;